#include "rtmp_pusher.h"
#include "process_frame.h"

RtmpPusher* RtmpPusher::m_pInstance = nullptr;

RtmpPusher::RtmpPusher()
{
    _infmt_ctx = nullptr;
    _outfmt_ctx = nullptr;
    _encodec_ctx = nullptr;
    _decodec_ctx = nullptr;
    out_stream = nullptr;
    struct SwsContext *img_convert_ctx  = NULL;
    memset(rtmp_url, 0, sizeof(rtmp_url));
}

RtmpPusher* RtmpPusher::GetInstance()
{
    if(m_pInstance == nullptr)
        m_pInstance = new RtmpPusher();
    return m_pInstance;
}




int RtmpPusher::create_av_context(AVDictionary *options)
{
    //视频输入格式
    AVInputFormat* ifmt =NULL;
    ifmt = av_find_input_format("video4linux2");

    //视频输入设备
    char *in_filename  = "/dev/video12";
    
    //视频输出设备
    strcpy(rtmp_url, "rtmp://127.0.0.1/live/stream");
    
    //打开视频设备
    printf("opening video device\n");
    if (0 > avformat_open_input(&_infmt_ctx, in_filename, ifmt, &options)) {
        printf("failed open input file\n");
        return -1;
    }
    
    //读取设备信息
    printf("reading video device info\n");
    if (0 > avformat_find_stream_info(_infmt_ctx, NULL)) {
        printf("failed find stream info\n");
        avformat_close_input(&_infmt_ctx);
        return -1;
    }

    //查找视频||音频流
    printf("finding video stream\n");
    for (int i = 0; i < _infmt_ctx->nb_streams; i++)
    {
        //Create output AVStream according to input AVStream
        
        if (_infmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            video_idx = i;
        }
        else
        {
            break;
        }
    }
    
    if (video_idx == -1) 
    {
        printf("input video stream not exist\n");
        return -1;
    }

    //初始化输出封装器
    printf("outputing context\n");
    int ret = avformat_alloc_output_context2(&_outfmt_ctx, NULL, "flv", rtmp_url);
    if (ret != 0) {
        printf("failed alloc output context\n");
        avformat_close_input(&_infmt_ctx);
        return -1;
    } 

    return 0;
}


int RtmpPusher::create_encoder()
{
    AVCodec* encodec = NULL;
    //找到编码器
    printf("finding encoder\n");
    //encodec = avcodec_find_encoder(AV_CODEC_ID_H264);
    encodec = avcodec_find_encoder_by_name("libx264");
    if (!encodec) 
    {
        printf("not find encoder\n");
        avformat_close_input(&_infmt_ctx);
        return -1;
    }

    //创建编码器
    printf("creating encodec\n");
    _encodec_ctx = avcodec_alloc_context3(encodec);
    if (!_encodec_ctx) 
    {
        printf("not alloc context3\n\n");
        avformat_close_input(&_infmt_ctx);
        return -1;
    }

    //配置编码器参数
    _encodec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; 
    _encodec_ctx->codec_id = encodec->id;
    _encodec_ctx->thread_count = 8;
    //_encodec_ctx->bit_rate = 800000;
    _encodec_ctx->width = 640;
    _encodec_ctx->height = 480;
    _encodec_ctx->time_base = (AVRational){1, 30};    //5是编多少帧就发送，可根据编码速度改变
    _encodec_ctx->framerate = (AVRational){30, 1};
    _encodec_ctx->gop_size = 250;
    _encodec_ctx->max_b_frames = 0;
    _encodec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;

    //编码质量和速度
    printf("set encodec param\n");
    av_opt_set(_encodec_ctx->priv_data, "preset", "ultrafast", 0);
    av_opt_set(_encodec_ctx->priv_data, "tune", "zerolatency", 0);


    AVDictionary *opts = NULL;
    av_dict_set(&opts, "profile", "baseline", 0);
    //av_opt_set(_encodec_ctx->priv_data, "crf", "18", 0);
    
    //打开编码器
    printf("opening encodec\n");
    int ret = avcodec_open2(_encodec_ctx, encodec, &opts);
    if (ret < 0) {
        char achErrMsg[1024] = {0};
        av_make_error_string(achErrMsg, sizeof(achErrMsg), ret);
        fprintf(stderr, "Could not open codec: %s\n", achErrMsg);
        return -1;
    }
}

int RtmpPusher::create_decoder()
{
    AVCodec* decodec = NULL;

    
    
    _decodec_ctx = _infmt_ctx->streams[video_idx]->codec;
    //找到解码器
    printf("finding decoder\n");
    decodec = avcodec_find_decoder(_decodec_ctx->codec_id);
    if (!decodec) 
    {
        printf("not find decoder\n");
        avformat_close_input(&_infmt_ctx);
        return -1;
    }

    //打开解码器
    printf("opening dencodec\n");
    int ret = avcodec_open2(_decodec_ctx, decodec, NULL);
    if (ret < 0) {
        char achErrMsg[1024] = {0};
        av_make_error_string(achErrMsg, sizeof(achErrMsg), ret);
        fprintf(stderr, "Could not open codec: %s\n", achErrMsg);
        return -1;
    }
}


int RtmpPusher::create_av_stream()
{
    
    //添加视频流
    printf("new stream\n");
    out_stream = avformat_new_stream(_outfmt_ctx,NULL);
    if (!out_stream) {
        printf("failed new stream\n");
        avformat_close_input(&_infmt_ctx);
        avformat_close_input(&_outfmt_ctx);
        return -1;
    }
    out_stream->codecpar->codec_tag = 0;
    //复制参数
    avcodec_parameters_from_context(out_stream->codecpar, _encodec_ctx);
    
    //查看输出封装内容
    printf("====\n");
    av_dump_format(_infmt_ctx, 0, "usb camera", 0);
    
    printf("====\n");
    av_dump_format(_outfmt_ctx, 0, rtmp_url, 1);

    //打开rtmp的网络输出IO
    printf("opening avio\n");
    int ret=avio_open(&_outfmt_ctx->pb, rtmp_url, AVIO_FLAG_WRITE);
    if (ret!=0) {
        printf("failed to open outfile\n");
        avformat_close_input(&_infmt_ctx);
        avformat_close_input(&_outfmt_ctx);
        return -1;
    }
    
    //写入封装头
    printf("writing av header\n");
    ret = avformat_write_header(_outfmt_ctx, NULL);
    if (ret!=0) {
        printf("failed to write header\n");
        avio_close(_outfmt_ctx->pb);
        avformat_close_input(&_infmt_ctx);
        avformat_close_input(&_outfmt_ctx);
        return -1;
    }
}

int RtmpPusher::create_av_pack()
{
    
    //包裹申请内存
    printf("creating dec_pkt, enc_pkt\n");
    dec_pkt = (AVPacket *)av_malloc(sizeof(AVPacket)); 
    //enc_pkt = (AVPacket *)av_malloc(sizeof(AVPacket)); 
    memset(&enc_pkt, 0, sizeof(enc_pkt));

    //像素格式转换YU420
    printf("sws_getCachedContext\n");
    img_convert_ctx  = sws_getCachedContext(img_convert_ctx, _encodec_ctx->width, _encodec_ctx->height,_encodec_ctx->pix_fmt, _encodec_ctx->width, _encodec_ctx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, 0, 0, 0);
    if (!img_convert_ctx)
    {
        printf("fail to sws_getCachedContext\n");
    } 
    
}


int RtmpPusher::create_av_frame()
{
    printf("RtmpPusher::create_av_frame\n");
    //原始帧
    __frame = av_frame_alloc();
    //输出帧
    __frame_yuv = av_frame_alloc();
    __frame_yuv->format = AV_PIX_FMT_YUV420P;
    __frame_yuv->width = 640;
    __frame_yuv->height = 480;
    __frame_yuv->pts = 0;
    
    int ret = av_frame_get_buffer(__frame_yuv, 1);
    if (ret != 0)
    {
        printf("fail to frame get buffer\n");
        return -1;
    }
    
}




int RtmpPusher::Init(const char* url)
{
    //初始化网络
    avformat_network_init();

    //初始化设备
    avdevice_register_all();

    AVDictionary *av_options = nullptr;
    av_dict_set(&av_options, "video_size", "640x480", 0);
    av_dict_set(&av_options, "framerate", "30", 0);
    av_dict_set(&av_options, "pixel_format", "mjpeg", 0);

    if( 0 > create_av_context(av_options) )
        return -1;
    

    create_encoder();
    create_decoder();
    create_av_stream();
    create_av_pack();
    create_av_frame();
}


int RtmpPusher::Start()
{
    printf("RtmpPusher::Start\n");
    //开始计时
    int64_t start_time = av_gettime();
    //标记
    int got_picture=0,enc_got_frame=0;
    //每一帧编号
    int vpts = 0;

    int64_t frame_delay = av_rescale_q(1, _encodec_ctx->time_base, AV_TIME_BASE_Q);
    int64_t next_frame_time = av_gettime() + frame_delay;

    while(1)
    {
        //每一帧加1
        __frame_yuv->pts = vpts;
        vpts += 1;
        //获取摄像头帧
        //printf("av_read_frame\n");
        int ret = av_read_frame(_infmt_ctx,dec_pkt);
        if (ret != 0)
        {    
            printf("fail to read_frame\n");
            break;
        }
        
        //解码获取初始图片
        //printf("avcodec_decode_video2\n");
        ret = avcodec_decode_video2(_infmt_ctx->streams[dec_pkt->stream_index]->codec, __frame, &got_picture, dec_pkt);
        if(!got_picture)
        {
            printf("123\n");
            continue;
        }
        //h264格式转换
        //printf("sws_scale\n");
        ret = sws_scale(img_convert_ctx, (const uint8_t* const*)__frame->data, __frame->linesize, 0, _encodec_ctx->height, __frame_yuv->data, __frame_yuv->linesize);
        if (ret <= 0)
        {
            printf("123\n");
            continue;
        }

        //FrameProcessor* pFrameProcessor = FrameProcessor::getInstance();
        //pFrameProcessor->ProcessYUVFrame(__frame_yuv);

        
        //输出帧编码
        //printf("avcodec_send_frame\n");
        ret = avcodec_send_frame(_encodec_ctx, __frame_yuv);
        if (ret != 0)
        {
            printf("123\n");
            continue;
        }
        //打包到输出包裹
        //printf("avcodec_receive_packet\n");
        ret = avcodec_receive_packet(_encodec_ctx, &enc_pkt);
        if (ret != 0 || enc_pkt.size > 0)
        {
                //cout << "*" << pack.size << flush;
        }
        else
        {
                continue;
        }
        
        //推流
        // enc_pkt.pts = av_rescale_q(enc_pkt.pts, _encodec_ctx->time_base, out_stream->time_base);
        // enc_pkt.dts = av_rescale_q(enc_pkt.dts, _encodec_ctx->time_base, out_stream->time_base);
        // enc_pkt.duration = av_rescale_q(enc_pkt.duration, _encodec_ctx->time_base, out_stream->time_base);

        //推流
        enc_pkt.pts = av_rescale_q(__frame_yuv->pts, _encodec_ctx->time_base, out_stream->time_base);
        enc_pkt.dts = av_rescale_q(__frame_yuv->pts, _encodec_ctx->time_base, out_stream->time_base);
        enc_pkt.duration = av_rescale_q(1, _encodec_ctx->time_base, out_stream->time_base);
        
        //发送到服务器
        //printf("av_interleaved_write_frame\n");
        ret = av_interleaved_write_frame(_outfmt_ctx, &enc_pkt);
        if (ret < 0) {
            fprintf(stderr, "Error muxing packet\n");
            break;
        }	
        //查看第几帧
        //printf("%d\n",vpts);
        
        //printf("av_packet_unref\n");
        av_packet_unref(&enc_pkt);
        //printf("continue\n");

        // 控制帧率
        // int64_t current_time = av_gettime();
        // if (current_time < next_frame_time) {
        // 	av_usleep(next_frame_time - current_time);
        // }
        // next_frame_time += frame_delay;
        // printf("Frame %d sent, pts: %ld, dts: %ld\n", vpts, enc_pkt.pts, enc_pkt.dts);
        
    }
    avio_close(_outfmt_ctx->pb);
    avformat_close_input(&_infmt_ctx);
    avformat_close_input(&_outfmt_ctx);
    return 0;
}


int RtmpPusher::PushAudio(unsigned char* data, int size, int64_t pts)
{
    return 0;
}


int RtmpPusher::PushVideo(unsigned char* data, int size, int64_t pts)
{
    return 0;
}